SuperStrict

Framework BRL.FileSystem
Import BRL.System
Import BRL.StandardIO

Global assetFolder:String = "..\assets\"
'Global assetFolder:String = "D:\SteamLibrary\steamapps\common\Starbound\assets\"

Function isWhitespace:Int(source:String)

	Local index:Int = 0
	While index < source.length
		Select Chr(source[index])
			Case "~t"
				index = index + 1
			Case "~n"
				index = index + 1
			Case "~r"
				index = index + 1
			Case " "
				index = index + 1
			Case ","
				index = index + 1
			Default
				Return False
		End Select
	Wend
	Return True

End Function

Function FindWhitespace:Int(source:String, start:Int)

	Local index:Int = start
	Local char:String = Chr(source[index])
	While Not isWhitespace(char)
		index = index + 1
		If index >= source.length Then
			Return source.length - 1
		Else
			char = Chr(source[index])
		EndIf
	Wend
	
	Return index - 1

End Function

Function FindMatching:Int(source:String, start:Int)

	Local index:Int = start + 1
	Local match:String
	Select Chr(source[start])
		Case "~q"
			match = "~q"
		Case "{"
			match = "}"
		Case "["
			match = "]"
		Default
			Return source.length - 1
	End Select
	
	Local char:String = Chr(source[index])
	While char <> match
		If (char = "~q") Or (char = "{") Or (char = "[") Then
			index = FindMatching(source, index) + 1
		Else
			index = index + 1
		EndIf
		If index >= source.length Then
			Return source.length - 1
		Else
			char = Chr(source[index])
		EndIf
	Wend
	
	Return index

End Function

Function ParseJSON:String(source:String, nameSpace:String, depth:Int = 0)

	Local index:Int
	Local margin:String = ""
	For index = 1 To depth
		margin = margin + "~t"
	Next
	
	Local dest:String
	If nameSpace.EndsWith(".") Then
		nameSpace = nameSpace[0..(nameSpace.length - 1)]
	EndIf
	
	Local inItem:Int = False
	Local itemKey:String = ""
	Local itemValue:String = ""
	Local itemIndex:Int = 0
	
	Local blockEnd:Int = 0
	Local subString:String = ""
	
	index = 0
	Local char:String
	While index < source.length
		char = Chr(source[index])
		Select char
			Case "{"
				blockEnd = FindMatching(source, index)
				If blockEnd > index + 1 Then
					If (Not itemKey) And (depth > 0) Then
						subString = "{}~n" + ParseJSON(source[(index + 1)..blockEnd], nameSpace + "[" + String.FromInt(itemIndex + 1) + "]", depth + 1)
					Else
						subString = "{}~n" + ParseJSON(source[(index + 1)..blockEnd], nameSpace + "." +  itemKey.Replace("~q", ""), depth + 1)
					EndIf
				Else
					substring = "{}"
				EndIf
				inItem = True
				index = blockEnd + 1
			Case "["
				blockEnd = FindMatching(source, index)
				If blockEnd > index + 1 Then
					If (Not itemKey) And (depth > 0) Then
						subString = "{}~n" + ParseJSON(source[(index + 1)..blockEnd], nameSpace + "[" + String.FromInt(itemIndex + 1) + "]", depth + 1)
					Else
						subString = "{}~n" + ParseJSON(source[(index + 1)..blockEnd], nameSpace + "." + itemKey.Replace("~q", ""), depth + 1)
					EndIf
				Else
					subString = "[]"
				EndIf
				inItem = True
				index = blockEnd + 1
			Case "~q"
				blockEnd = FindMatching(source, index)
				subString = source[index..(blockEnd + 1)]
				index = blockEnd + 1
			Case ":"
				inItem = True
				index = index + 1
			Default
				If isWhitespace(char) Then
					index = index + 1
				Else
					blockEnd = FindWhitespace(source, index)
					subString = source[index..(blockEnd + 1)]
					index = blockEnd + 1
				EndIf
		End Select
		If subString Then
			If inItem Then
				itemValue = subString
				If Not itemKey Then
					If depth > 0 Then
						itemIndex = itemIndex + 1
						itemKey = "[" + String.FromInt(itemIndex) + "]"
					EndIf
				Else
					itemKey = "." + itemKey
				EndIf
				dest = dest + margin + nameSpace + itemKey.Replace("~q", "") + " = " + itemValue + "~n"
				itemKey = ""
				itemValue = ""
				inItem = False
			Else
				If itemKey Then
					itemValue = itemKey
					itemIndex = itemIndex + 1
					itemKey = "[" + String.FromInt(itemIndex) + "]"
					dest = dest + margin + nameSpace + itemKey.Replace("~q", "") + " = " + itemValue + "~n"
					itemKey = ""
					itemValue = ""
				EndIf
				itemKey = subString
			EndIf
			substring = ""
		EndIf
	Wend
	
	If itemKey Then
		If itemIndex > 0 Then
			itemValue = itemKey
			itemIndex = itemIndex + 1
			itemKey = "[" + String.FromInt(itemIndex) + "]"
			dest = dest + margin + nameSpace + itemKey.Replace("~q", "") + " = " + itemValue
		Else
			dest = dest + nameSpace + " = " + itemKey
		EndIf
	EndIf
	
	Return dest

End Function

Function FileToString:String(fname:String)

	Local fHnd:TStream = ReadFile(fname)
	
	If fHnd Then
		Local fileAsString:String
		Local nextLine:String
		Local subStrings:String[]
		While Not Eof(fHnd)
			nextLine = ReadLine(fHnd).Trim()
			subStrings = nextLine.Split("//")
			fileAsString = fileAsString + subStrings[0].Trim()
		Wend
		CloseFile(fHnd)
		
		Return fileAsString
	EndIf

End Function

Function ReadAllFilesOfType:String[](folder:String, ext:String)

	If Not folder.EndsWith("\") Then folder = folder + "\"
	Local matchList:String[]
	Local fileList:String[] = LoadDir(folder)
	Local file:String = ""
	
	For file = EachIn fileList
		Select FileType(folder + file)
			Case FILETYPE_DIR
				matchList = matchList + ReadAllFilesOfType(folder + file, ext)
			Case FILETYPE_FILE
				If ExtractExt(folder + file) = ext Then
					matchList = matchlist + [ParseJSON(FileToString(folder + file), file.Replace(".", "_"))]
				EndIf
		End Select
	Next
	
	Return matchList

End Function

Function StoreItemName:CombinedMaterial[](source:String, matList:CombinedMaterial[])

	Local lines:String[] = source.Split("~n")
	Local nextLine:String
	Local subLines:String[]
	Local key:String, val:String
	Local itemName:String = "", materialId:Int = -1
	
	For nextLine = EachIn lines
		subLines = nextLine.Split("=")
		key = subLines[0].Trim()
		If sublines.length > 1 Then
			val = subLines[1].Trim()
			If key.EndsWith(".itemName") Then
				itemName = val
			ElseIf key.EndsWith(".materialId") Then
				materialId = val.ToInt()
			EndIf
		EndIf
	Next
	If (materialId > -1) And (itemName <> "") Then
		If matList.length < materialId + 1 Then
			matlist = matlist[..materialId + 1]
		EndIf
		If matlist[materialId] = Null Then matlist[materialId] = New CombinedMaterial
		matlist[materialId].itemName = itemName
	EndIf
	
	Return matlist

End Function

Function StoreMaterialName:CombinedMaterial[](source:String, matList:CombinedMaterial[])

	Local lines:String[] = source.Split("~n")
	Local nextLine:String
	Local subLines:String[]
	Local key:String, val:String
	Local materialName:String = "", materialId:Int = -1
	
	For nextLine = EachIn lines
		subLines = nextLine.Split("=")
		key = subLines[0].Trim()
		If subLines.length > 1 Then
			val = subLines[1].Trim()
			If key.EndsWith(".materialName") Then
				materialName = val
			ElseIf key.EndsWith(".materialId") Then
				materialId = val.ToInt()
			EndIf
		EndIf
	Next
	If (materialId > -1) And (materialName <> "") Then
		If matList.length < materialId + 1 Then
			matlist = matlist[..materialId + 1]
		EndIf
		If matlist[materialId] = Null Then matlist[materialId] = New CombinedMaterial
		matlist[materialId].materialName = materialName
	EndIf
	
	Return matlist

End Function

Type CombinedMaterial
	Field itemName:String
	Field materialName:String
End Type

Print "Working, this may take a while..."

Local materialList:CombinedMaterial[]

Local matches:String[] = ReadAllFilesOfType(assetFolder, "matitem")
matches = matches + ReadAllFilesOfType(assetFolder, "material")

Print "Done parsing " + matches.length + " files, assembling lua table..."

Local json:String
For json = EachIn matches
	materialList = StoreItemName(json, materialList)
	materialList = StoreMaterialName(json, materialList)
Next

Print "Outputting to matsTable.lua..."
Local fHnd:TStream = WriteFile("matsTable.lua")
If fHnd Then
	WriteLine(fHnd, "-- to look up a material, use matsTable[~qblockName~q]")
	WriteLine(fHnd, "-- some blocks have no corresponding material item, these will return nil")
	WriteLine(fHnd, "-- some material items don't correspond with a block, these are omitted since we should never have to deal with them")
	WriteLine(fHnd, "matsTable = {}")
	Local i:Int
	For i = 0 To materialList.length - 1
		If materialList[i] <> Null Then
			If materialList[i].materialName <> "" Then
				If materialList[i].itemName <> "" Then
					WriteLine(fHnd, "~tmatsTable[" + materialList[i].materialName + "] = " + materialList[i].itemName)
				Else
					WriteLine(fHnd, "~tmatsTable[" + materialList[i].materialName + "] = nil")
				EndIf
			EndIf
		EndIf
	Next
	CloseFile(fHnd)
	Print "Finished!"
Else
	Print "Error: Could not write output file ~qmatsTable.lua~q"
EndIf
Input("Press ENTER to exit")
end
